﻿using System;
using System.ComponentModel;
using System.Drawing.Design;
using System.Text;
using Argos.Design;

namespace Argos.Scheduling
{
	/// <summary>
	/// Represents a time slot with a start time and duration.
	/// </summary>
	[Serializable]
	public struct TimeSlot : IComparable , IComparable<TimeSlot>
	{
		
		public static bool operator >(TimeSlot ts1, TimeSlot ts2)
		{
			string s1 = string.Format("{0}-{1}", ts1.Start.ToUtcTime(), ts1.Duration);
			string s2 = string.Format("{0}-{1}", ts2.Start.ToUtcTime(), ts2.Duration);

			return string.Compare(s1, s2) > 0;
		}

		public static bool operator <(TimeSlot ts1, TimeSlot ts2)
		{
			string s1 = string.Format("{0}-{1}", ts1.Start.ToUtcTime(), ts1.Duration);
			string s2 = string.Format("{0}-{1}", ts2.Start.ToUtcTime(), ts2.Duration);

			return string.Compare(s1, s2) < 0;
		}

		public static bool operator ==(TimeSlot ts1, TimeSlot ts2)
		{
			string s1 = string.Format("{0}-{1}", ts1.Start, ts1.Duration);
			string s2 = string.Format("{0}-{1}", ts2.Start, ts2.Duration);

			return string.Compare(s1, s2) == 0;
		}

		public static bool operator !=(TimeSlot ts1, TimeSlot ts2)
		{
			return !(ts1 == ts2);
		}



		#region public TimeSpan Duration { get; set; }

		/// <summary>
		/// Sets or returns the duration of the time slot.
		/// </summary>
		[Category("Time Slot")]
		[Description("The duration as a time span that specifies the length of the time slot. You can specify the duration as 'hh:mm:ss.nnn' where 'hh' is the number of hours, 'mm' is the number of minutes, 'ss' is the number of seconds and 'nnn' is the number of milliseconds.")]
		public TimeSpan Duration { get; set; }

		#endregion
		
		#region public TimeOfDay End { get; }

		/// <summary>
		/// Returns the end time of the time slot.
		/// </summary>
		[Browsable(false)]
		public TimeOfDay End
		{
			get { return this.Start.Add(this.Duration); }
		}

		#endregion
		
		#region public TimeOfDay Start { get; set; }

		/// <summary>
		/// Sets or returns the start time of the time slot.
		/// </summary>
		[Category("Time Slot")]
		[Description("The time of day that defines the start of the time slot. The time of day is formatted as 'hh:mm(:ss(.nnn))(Z) where 'hh' is the hour component, 'mm' is the minute component, 'ss' is the second component, 'nnn' is the millisecond component and 'Z' specifies that the time is in UTC. If 'Z' is missing, the time is in local time.")]
		public TimeOfDay Start { get; set; }

		#endregion



		#region public static TimeSlot FromStartTime(TimeOfDay start)

		/// <summary>
		/// Creates a new time slot from the specified start time with a zero duration.
		/// </summary>
		/// <param name="start">The start time for the time slot.</param>
		public static TimeSlot FromStartTime(TimeOfDay start)
		{
			return TimeSlot.FromStartTime(start, TimeSpan.Zero);
		}

		#endregion
		
		#region public static TimeSlot FromStartTime(TimeOfDay start, TimeSpan duration)

		/// <summary>
		/// Creates a new time slot from the specified start time with the
		/// given duration.
		/// </summary>
		/// <param name="start">The start time for the time slot.</param>
		/// <param name="duration">The duration for the time slot.</param>
		public static TimeSlot FromStartTime(TimeOfDay start, TimeSpan duration)
		{
			TimeSlot slot = new TimeSlot();
			slot.Start = start;
			slot.Duration = duration;
			return slot;
		}

		#endregion



		public int CompareTo(object obj)
		{
			if (obj is TimeSlot)
			{
				return this.CompareTo((TimeSlot)obj);
			}
			return 1;
		}

		public int CompareTo(TimeSlot other)
		{
			TimeSlot t1 = TimeSlot.FromStartTime(this.Start.ToUtcTime(), this.Duration);
			TimeSlot t2 = TimeSlot.FromStartTime(other.Start.ToUtcTime(), other.Duration);

			return t1.ToString().CompareTo(t2.ToString());
		}

		#region public override bool Equals(object obj)

		/// <summary>
		/// Returns true if the given object equals the current time slot.
		/// </summary>
		/// <param name="obj">The object to compare to the current.</param>
		public override bool Equals(object obj)
		{
			if (obj is TimeSlot)
			{
				TimeSlot ts = (TimeSlot)obj;
				return this.Start.Equals(ts.Start) && this.Duration.Equals(ts.Duration);
			}
			return false;
		}

		#endregion
		
		#region public override int GetHashCode()

		/// <summary>
		/// Returns the hash code for the current instance.
		/// </summary>
		public override int GetHashCode()
		{
			return this.ToString().GetHashCode();
		}

		#endregion
		
		#region public override string ToString()

		/// <summary>
		/// Returns the string representation of the time slot.
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			StringBuilder sb = new StringBuilder();

			sb.Append(this.Start.ToString());
			if (this.Duration > TimeSpan.Zero)
			{
				sb.AppendFormat(" to {0}", this.Start.Add(this.Duration));
			}

			return sb.ToString();
		}

		#endregion



	}
}
